<?php
/**
 * 유틸리티 - CUrl Request
 *
 * <code>
 * <?php
 * $utilCurl = utilCurl::getInstance();
 * $utilCurl->init(array('url' => 'http://xe2015test.cafe24.com'));
 * $utilCurl->exec();
 * ?>
 * </code>
 *
 * @package    util
 * @author     ul jung
 */
class utilCurl
{
    private $curl;

    private $aCurlOption;

    private $isGetinfo;

    /**
     * CUrl 인스턴스 반환
     *
     * @return utilCurl
     */
    public static function getInstance()
    {
        return utilSplClass::load('utilCurl');
    }

    /**
     * CURL 옵션 초기화 설정
     *
     * <code>
     * $aResult = utilCurl::getInstance()->init(array('url' => 'http://xe2015test.cafe24.com'))->exec();
     * </code>
     *
     * @param array $aParam array(
     *   'url' => 요청 URL 주소 (예 : http://www.test.com/curl.php)
     *   'host' => 호스트 주소 (예 : www.test.com)
     *   'header' => CURLOPT_HTTPHEADER 설정값
     *   'agent' => USER_AGENT 설정값
     *   'method' =>  GET | POST | DELET | PUT | HEAD (기본 : GET)
     *   'referer' => CURLOPT_REFERER 설정값
     *   'cookie' =>  CURLOPT_COOKIE 설정값
     *   'timeout' => CURLOPT_TIMEOUT 설정값
     *   'accept-charset' => 헤더상의 캐릭터셋 설정
     *   'post_fields' => POST 데이터 (예 : array('name' => '가나다라alex+&%$#$%^^^&^*$ABCD', 'age' => 18))
     *   'is_getinfo' => curl_getinfo 결과를 같이 반환받을 건지 여부 (기본 : false)
     * );
     *
     * @return utilCurl
     */
    public function init($aParam)
    {
        $this->curl = curl_init();
        $this->setBase();
        $this->setMethod($aParam['method']);
        $this->setHeader($aParam);
        $this->setReferer($aParam['referer']);
        $this->setAgent($aParam['agent']);
        $this->setCookie($aParam['cookie']);
        $this->setPost($aParam);
        $this->setUrl($aParam['url']);
        $this->setTimeout($aParam['timeout']);

        $this->isGetinfo = $aParam['is_getinfo'] === true ? true : false;

        foreach ($this->aCurlOption as $sKey => $mValue) {
            curl_setopt($this->curl, $sKey, $mValue);
        }

        return $this;
    }

    /**
     * CURL 요청 실행
     *
     * 실패시에는 array('curl_error' => 에러메세지, 'curl_error_code' => 에러코드) 가 반환됨
     *
     * @link http://kr1.php.net/manual/en/function.curl-getinfo.php
     * @link http://curl.haxx.se/libcurl/c/libcurl-errors.html
     * @return array(
     *   'header' => 반환된 헤더 문자열
     *   'body' => 반환된 본문 문자열
     *   'http_code' => HTTP 반환 코드 (예 : 200)
     *   'last_url' => 요청 URL (init 메소드의 url 입력값이 할당됨)
     *   'info' => curl_getinfo 전체 결과 (init 메소드에서 해당 설정이 활성화 되었을 경우만 할당됨)
     * )
     */
    public function exec()
    {
        $response = curl_exec($this->curl);
        $iErrorCode = curl_errno($this->curl);

        $aResult = array();

        if ($iErrorCode !== CURLE_OK) {
            $aResult['curl_error'] = curl_error($this->curl);
            $aResult['curl_error_code'] = $iErrorCode;
            return $aResult;
        }

        $iHeaderSize            = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE);

        $aResult['header']      = substr($response, 0, $iHeaderSize);
        $aResult['body']        = substr($response, $iHeaderSize);
        $aResult['http_code']   = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
        $aResult['last_url']    = curl_getinfo($this->curl, CURLINFO_EFFECTIVE_URL);

        if ($this->isGetinfo === true) {
            $aResult['info'] = curl_getinfo($this->curl);
        }

        curl_close($this->curl);

        return $aResult;
    }

    /**
     * CURL 옵션 설정값을 오버라이딩 (curl_setopt 함수의 래퍼)
     *
     * !!NOTICE!!
     * init 메소드 실행후에 해당 메소드를 사용하면 옵션값을 오버라이딩 할 수 있다.
     * 혹은 init 메소드에서 제공되지 않는 옵션을 세팅하고 싶을 때 사용할 수 있다.
     *
     * @link http://kr1.php.net/manual/en/function.curl-setopt.php
     * @param string $sKey
     * @param mixed $mValue
     * @return boolean
     */
    public function overrideOpt($sKey, $mValue)
    {
        return curl_setopt($this->curl, $sKey, $mValue);
    }

    private function setBase()
    {
        $this->aCurlOption = array(
            CURLOPT_RETURNTRANSFER  => 1,
            CURLOPT_HEADER          => 1,
            CURLOPT_FOLLOWLOCATION  => 1,
            CURLOPT_SSL_VERIFYPEER  => 0,
            CURLOPT_SSL_VERIFYHOST  => 0
        );
    }

    private function setMethod($sMethod)
    {
        $sMethod = empty($sMethod) ? 'GET' : $sMethod;

        if ($sMethod == 'HEAD') {
            $this->aCurlOption[CURLOPT_NOBODY] = 1;
        }
    }

    private function setReferer($sReferer)
    {
        if ($sReferer) {
            $this->aCurlOption[CURLOPT_REFERER] = $sReferer;
        }
    }

    private function setUrl($sUrl)
    {
        $this->aCurlOption[CURLOPT_URL] = $sUrl;
    }


    private function setAgent($sAgent)
    {
        if (empty($sAgent) === true) {
            $sAgent = '';
        }

        $this->aCurlOption[CURLOPT_USERAGENT] = $sAgent;
    }

    private function setHeader($aParam)
    {
        $aHeader = array(
            "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
            "Accept-Language: ru-ru,ru;q=0.7,en-us;q=0.5,en;q=0.3",
            "Keep-Alive: 300"
        );

        if (isset($aParam['accept-charset'])) {
            $aHeader[] = 'Accept-Charset: '.$aParam['accept-charset'];
        } else {
            $aHeader[] = 'Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7';
        }

        if (isset($aParam['host'])) {
            $aHeader[] = 'Host: '.$aParam['host'];
        }

        if (isset($aParam['header'])) {
            $aHeader[] = $aParam['header'];
        }

        $this->aCurlOption[CURLOPT_HTTPHEADER] = $aHeader;
    }

    private function setCookie($sCookie)
    {
        if ($sCookie['cookie']) {
            $this->aCurlOption[CURLOPT_COOKIE] = $sCookie;
        }
    }

    private function setPost($aParam)
    {
        if (strtoupper($aParam['method']) == 'POST') {
            $this->aCurlOption[CURLOPT_POST]        = true;
            $this->aCurlOption[CURLOPT_POSTFIELDS]  = $aParam['post_fields'];
        }
    }

    private function setTimeout($iTimeout)
    {
        $iTimeout = ($iTimeout) ? $iTimeout : 5;
        $this->aCurlOption[CURLOPT_TIMEOUT] = $iTimeout;
    }
}